
package com.smedi.ismedi.inmail.core.application.service.impl;

import cn.hutool.core.util.IdUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.smedi.ismedi.authority.starter.util.VerifyHelper;
import com.smedi.ismedi.inmail.core.application.service.LoginLogOrgStaticsApplicationService;
import com.smedi.ismedi.inmail.core.domain.aggregatemodel.LoginLogOrgStatics;
import com.smedi.ismedi.inmail.core.domain.aggregatemodel.LoginLogUserStatics;
import com.smedi.ismedi.inmail.core.port.adapter.persistence.model.LoginLogEntity;
import com.smedi.ismedi.inmail.core.port.adapter.persistence.model.LoginLogOrgStaticsEntity;
import com.smedi.ismedi.inmail.core.port.adapter.persistence.mybatis.mapper.LoginLogMapper;
import com.smedi.ismedi.inmail.core.port.adapter.persistence.mybatis.mapper.LoginLogOrgStaticsMapper;
import com.smedi.ismedi.inmail.core.port.adapter.service.FileRemoteApi;
import com.smedi.ismedi.organization.api.dto.request.OrganizationListQueryRequest;
import com.smedi.ismedi.organization.api.dto.response.resp.OrganizationResp;
import com.smedi.ismedi.organization.api.feign.RemoteHrOrganizationFeignApi;
import com.smedi.ismedi.personnel.api.dto.request.OrganizationAssignmentListQueryRequest;
import com.smedi.ismedi.personnel.api.dto.response.resp.OrganizationAssignmentResp;
import com.smedi.ismedi.personnel.api.feign.RemotePersonCoreFeignApi;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.BorderStyle;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.VerticalAlignment;
import org.apache.poi.xssf.usermodel.XSSFCellStyle;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.*;

import static com.smedi.ismedi.inmail.core.infrastructure.constant.ConstantField.COMPANY_MANAGEMENT_ORGANIZATION;
import static com.smedi.ismedi.inmail.core.infrastructure.constant.ConstantField.PROJECT_MANAGEMENT_ORGANIZATION;

/**
 * 通知公告应用服务实现类
 *
 * @author jiangqinxuan
 * @date 2021/07/26
 */
@Service
@RequiredArgsConstructor
@Transactional
@Slf4j
public class LoginLogOrgStaticsApplicationServiceImpl extends ServiceImpl<LoginLogOrgStaticsMapper, LoginLogOrgStaticsEntity>
        implements LoginLogOrgStaticsApplicationService {

    private final LoginLogMapper loginLogMapper;
    private final FileRemoteApi fileRemoteApi;
    private final RemotePersonCoreFeignApi remotePersonCoreFeignApi;
    private final RemoteHrOrganizationFeignApi remoteHrOrganizationFeignApi;

    @Override
    public void statics() {
        List<Long> removeIds = new ArrayList<>();
        List<LoginLogOrgStaticsEntity> staticsCreateds = new ArrayList<>();
        List<LoginLogOrgStaticsEntity> staticsUpdateds = new ArrayList<>();

        List<OrganizationResp> organizations = remoteHrOrganizationFeignApi.getOrganizationList(OrganizationListQueryRequest
                .builder().typeList(new ArrayList<>(Arrays.asList(COMPANY_MANAGEMENT_ORGANIZATION,PROJECT_MANAGEMENT_ORGANIZATION))).build()).getData();
        Map<Long, OrganizationResp> idOrganizationMap = new HashMap<>();
        for (OrganizationResp organization : organizations) {
            idOrganizationMap.put(organization.getId(), organization);
        }
        List<OrganizationAssignmentResp> organizationAssignments = remotePersonCoreFeignApi.getOrganizationAssignmentList(OrganizationAssignmentListQueryRequest.builder().build()).getData();
        Map<Long, Integer> organizationIdPersonNumberMap = new HashMap<>();
        Map<Long, List<Long>> organizationIdPersonIdsMap = new HashMap<>();
        for (OrganizationAssignmentResp organizationAssignment : organizationAssignments) {
            Long hrOrganizationId = organizationAssignment.getHrOrganizationId();
            organizationIdPersonNumberMap.put(hrOrganizationId, organizationIdPersonNumberMap.getOrDefault(hrOrganizationId, 0) + 1);
            List<Long> personIds = organizationIdPersonIdsMap.getOrDefault(hrOrganizationId, new ArrayList<>());
            personIds.add(organizationAssignment.getPersonId());
            organizationIdPersonIdsMap.put(hrOrganizationId, personIds);
        }
        List<LoginLogEntity> loginLogs = loginLogMapper.selectList(new LambdaQueryWrapper<LoginLogEntity>()
                .ge(LoginLogEntity::getLoginTime, LocalDateTime.of(LocalDate.now().minusDays(1), LocalTime.MIDNIGHT))
                .lt(LoginLogEntity::getLoginTime, LocalDateTime.of(LocalDate.now(), LocalTime.MIDNIGHT)));
        Map<Long, Integer> personIdTotalNumberMap = new HashMap<>();
        Map<Long, Long> personIdTotalMinuteMap = new HashMap<>();
        for (LoginLogEntity loginLog : loginLogs) {
            Long personId = loginLog.getLoginUserId();
            personIdTotalNumberMap.put(personId, personIdTotalNumberMap.getOrDefault(personId, 0) + 1);
            personIdTotalMinuteMap.put(personId, personIdTotalMinuteMap.getOrDefault(personId, 0L) + Duration.between(loginLog.getLoginTime(), loginLog.getLogoutTime()).toMinutes());
        }
        List<LoginLogOrgStaticsEntity> loginLogOrgStaticsEntities = this.getBaseMapper().selectList(null);
        Map<Long, LoginLogOrgStaticsEntity> orgIdStaticsMap = new HashMap<>();
        for (LoginLogOrgStaticsEntity statics : loginLogOrgStaticsEntities) {
            Long orgId = statics.getOrganizationId();
            OrganizationResp organization = idOrganizationMap.get(orgId);
            if (null == organization) {
                removeIds.add(statics.getId());
            }
            orgIdStaticsMap.put(orgId, statics);
        }
        for (OrganizationResp organization : organizations) {
            Long orgId = organization.getId();
            LoginLogOrgStaticsEntity statics = orgIdStaticsMap.get(orgId);
            List<Long> personIds = organizationIdPersonIdsMap.getOrDefault(orgId, new ArrayList<>());
            int totalNumber = 0;
            long totalMinute = 0L;
            for (Long personId : personIds) {
                totalNumber = totalNumber + personIdTotalNumberMap.getOrDefault(personId, 0);
                totalMinute = totalMinute + personIdTotalMinuteMap.getOrDefault(personId, 0L);
            }
            if (null == statics) {
                staticsCreateds.add(LoginLogOrgStaticsEntity.builder()
                        .organizationId(orgId)
                        .organizationName(organization.getShortName())
                        .displayOrder(organization.getDisplayOrder())
                        .personNumber(organizationIdPersonNumberMap.getOrDefault(orgId, 0))
                        .totalNumber(totalNumber)
                        .totalMinute(totalMinute)
                        .build());
                continue;
            }
            LoginLogOrgStaticsEntity staticsUpdated = LoginLogOrgStaticsEntity.builder()
                    .organizationId(orgId)
                    .organizationName(organization.getShortName())
                    .displayOrder(organization.getDisplayOrder())
                    .personNumber(organizationIdPersonNumberMap.getOrDefault(orgId, 0))
                    .totalNumber(statics.getTotalNumber() + totalNumber)
                    .totalMinute(statics.getTotalMinute() + totalMinute)
                    .build();
            staticsUpdated.setId(statics.getId());
            staticsUpdateds.add(staticsUpdated);
        }

        if (VerifyHelper.isAvailable(removeIds)) {
            this.getBaseMapper().deleteBatchIds(removeIds);
        }
        saveBatch(staticsCreateds);
        updateBatchById(staticsUpdateds);
    }

    @Override
    @SneakyThrows
    public String export(List<LoginLogOrgStatics> loginLogOrgStatics) {
        String fileKey = "";
        ClassPathResource classPathResource = new ClassPathResource("fileTemplate/LoginLogUserStatics.xlsx");
        XSSFWorkbook workBook;
        try (InputStream inputStream = classPathResource.getInputStream()) {
            workBook = new XSSFWorkbook(inputStream);
        }
        XSSFSheet buildingSheet = workBook.cloneSheet(0, "登录统计");
        XSSFCellStyle cellStyle = workBook.createCellStyle();
        //启动单元格内换行
        cellStyle.setWrapText(true);
        cellStyle.setBorderBottom(BorderStyle.THIN);
        cellStyle.setBorderTop(BorderStyle.THIN);
        cellStyle.setBorderRight(BorderStyle.THIN);
        cellStyle.setBorderLeft(BorderStyle.THIN);
        cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
        cellStyle.setAlignment(HorizontalAlignment.CENTER);
        int i = 0;
        for (LoginLogOrgStatics loginLogOrgStatic : loginLogOrgStatics) {
            i++;
            XSSFRow rowAdd = buildingSheet.createRow(i);
            rowAdd.setHeight((short) 600);
            for (int j = 0; j < 7; j++) {
                rowAdd.createCell(j);
                rowAdd.getCell(j).setCellStyle(cellStyle);
            }
            rowAdd.getCell(0).setCellValue(i);
            rowAdd.getCell(1).setCellValue(loginLogOrgStatic.getOrganizationName());
            rowAdd.getCell(2).setCellValue(loginLogOrgStatic.getPersonNumber());
            rowAdd.getCell(3).setCellValue(loginLogOrgStatic.getTotalMinute());
            rowAdd.getCell(4).setCellValue(loginLogOrgStatic.getAvgMinute().toString());
            rowAdd.getCell(5).setCellValue(loginLogOrgStatic.getTotalMinute());
            rowAdd.getCell(6).setCellValue(loginLogOrgStatic.getAvgNumber().toString());
        }

        workBook.removeSheetAt(0);

        final File outputFile = File.createTempFile(IdUtil.simpleUUID(), ".xlsx");
        try (FileOutputStream outputStream = new FileOutputStream(outputFile)) {
            workBook.write(outputStream);
            fileKey = fileRemoteApi.uploadFile(outputFile);
        } catch (Exception e) {
            log.info(e.getMessage());
        } finally {
            Files.deleteIfExists(Paths.get(outputFile.getAbsolutePath()));
        }

        return fileKey;
    }
}
